package Derivation;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import Basics.*;
import Circuit.Circuit;

public class ParseResultsBottomUp 
{
	DeriveByProgramBottomUp programDeriver;
	
	Set<Atom> addedInstAtoms = new HashSet<Atom>();
	
	Map<Atom, HashSet<Body>> prov = new HashMap<Atom, HashSet<Body>>();
			
	Circuit c = new Circuit();
	
	
	
	public ParseResultsBottomUp (Rule ... irs)
	{
		this.programDeriver = new DeriveByProgramBottomUp (irs);
	}
	
	
	
	public ParseResultsBottomUp (Program p)
	{
		this.programDeriver = new DeriveByProgramBottomUp (p);
	}


	
	public Set<Atom> getAddedInstAtoms() 
	{
		return addedInstAtoms;
	}

	

	public Circuit getC() 
	{
		return c;
	}
	
	

	public Map<Atom, HashSet<Body>> getProv() 
	{
		return prov;
	}


	/*************************************************************************************************************/
	/** Title: ParseResults																				
	/** Description: finds all newly derived atoms from this step and adds them to this.addedInstAtoms			
	/*************************************************************************************************************/

	public boolean ParseResults (boolean forIntersection)
	{	
		//long startTime = System.currentTimeMillis();
		
		Set<Atom> compareVec = new HashSet (this.programDeriver.getAtomsAddedSoFar());
		
		this.programDeriver.DeriveFromProgramIterationBottomUp(forIntersection);	
		
		this.addedInstAtoms = new HashSet (this.programDeriver.getAtomsAddedSoFar());
		
		boolean retVal = false;
		if (false == compareVec.isEmpty()) //see if new atoms have been derived in this iteration
		{
			retVal = (compareVec.size() == this.addedInstAtoms.size());
		}
		
		this.addedInstAtoms.removeAll(compareVec); //for UpdateDB()
		
		//long startTime = System.currentTimeMillis();
		if (false == retVal) 
		{
			c.getCircuit().add(new HashMap<String, Atom>());//add new level to circuit
			for (Atom atom : this.programDeriver.getAddedInstAtomsInLastIteration()) 
			{
				if (true == forIntersection) 
				{
					c.AddNodeToCircuit(atom, this.programDeriver.getProvenanceProgram().get(atom), null, null, true, 0);
				}
				
				else
				{
					AddToProv(atom, this.programDeriver.getProvenanceProgram().get(atom));
				}
			}
		}
		
		else
		{
			this.programDeriver.atomsAddedSoFar.clear();
		}
		
		/*long end = System.currentTimeMillis();
		if ((end-startTime) > 1000) 
		{
			System.out.println("ParseResultsBottomUp:: time for circuit layer: " + (end-startTime));
			System.out.println("ParseResultsBottomUp:: num of added inst atoms: " + this.programDeriver.getAddedInstAtomsInLastIteration().size());
		}*/
		//System.out.println("added this time: " +  this.programDeriver.getAddedInstAtomsInLastIteration().size() + "level: " + programDeriver.level);
		return retVal;
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: UpdateDB																				
	/** Description: add newly derived facts to DB			
	/*************************************************************************************************************/
	
	public void UpdateDB ()
	{
		for (Atom atom : this.addedInstAtoms) 
		{
			DB.getInstance().Update(atom);
		}
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: Reset																				
	/** Description: resets the parameters for next step of the program			
	/*************************************************************************************************************/
	
	public void Reset ()
	{
		this.addedInstAtoms.clear();
		//this.programDeriver.Reset();
	}
	
	
	/*************************************************************************************************************/
	/** Title: AddToProv																				
	/** Description: 				
	/*************************************************************************************************************/
	
	public void AddToProv (Atom head, HashSet<Body> bodies)
	{
		if (false == this.prov.containsKey(head))
		{
			this.prov.put(head, new HashSet<Body>());
		}
		
		this.prov.get(head).addAll(bodies);
		/*System.out.println("bodies:");
		for (Body body : this.prov.get(head)) 
		{
			System.out.println(body.getAtoms());
		}*/
	}
	
	
	/*************************************************************************************************************/
	/** Title: GetProvSize																				
	/** Description: 				
	/*************************************************************************************************************/
	
	public int GetProvSize ()
	{
		int size = 0;
		for (HashSet<Body> set : this.prov.values()) 
		{
			size += set.size();
		}
		
		return size;
	}
	
}
